iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 6
1

Banner

原型模式 Prototype Pattern

原型模式,你有些物件可能會需要建立很多一樣的物件,只是某些資料不太一樣而已,就有點像是每顆大頭菜都是一模一樣的物件,但可能因為來自不同的島,所以每顆大頭菜的差別只在於那起始購買的鈴錢不同。

UML

UML

實作

首先我們需要以抽象類別的方式,來製作大頭菜的原型,並且定義好大頭菜的基本功能。

TurnipsPrototype.php

/**
 * Class TurnipsPrototype.
 */
abstract class TurnipsPrototype
{
    /**
     * @var string
     */
    protected $category;

    /**
     * @var int
     */
    protected $price;

    /**
     * @var int
     */
    protected $count;

    abstract public function __clone();

    /**
     * @return int
     */
    public function calculatePrice(): int
    {
        if (isset($this->price) && isset($this->count)) {
            return $this->price * $this->count;
        } else {
            return 0;
        }
    }
}

再來建立健康的大頭菜、壞掉的大頭菜,並且引用大頭菜原型,這裡需要注意我們須要去實作 __clone 方法,這樣子才能讓大頭菜複製、貼上。

Turnips.php

/**
 * Class Turnips.
 */
class Turnips extends TurnipsPrototype
{
    /**
     * @var string
     */
    protected $category = '大頭菜';

    /**
     * Turnips constructor.
     *
     * @param int $price
     * @param int $count
     */
    public function __construct(int $price, int $count)
    {
        $this->price = $price;
        $this->count = $count;
    }

    /**
     * ...
     */
    public function __clone()
    {
        // TODO: Implement __Clone() method.
    }
}

SpoiledTurnips.php

/**
 * Class SpoiledTurnips.
 */
class SpoiledTurnips extends TurnipsPrototype
{
    /**
     * @var string
     */
    protected $category = '壞掉的大頭菜';

    /**
     * 壞掉的大頭菜是沒辦法賣鈴錢的狸!
     * 
     * @var int
     */
    const SPOILED_PRICE = 0;

    /**
     * SpoiledTurnips constructor.
     *
     * @param int $price
     * @param int $count
     */
    public function __construct(int $price, int $count)
    {
        $this->price = self::SPOILED_PRICE;
        $this->count = $count;
    }

    /**
     * ...
     */
    public function __clone()
    {
        // TODO: Implement __Clone() method.
    }
}

測試

最後要來驗證一下我們所寫的原型是否是正確的,所以這次的測試也比較簡單,原型模式著重於大頭菜可以被複製,每個被複製出來的大頭菜只有局部的內容不一樣,以最節省資源的方式,來處理重複性質高的物件。

  1. 建立大頭菜,並且複製 10 次,檢查每次大頭菜是否都是大頭菜,而且價格是正確的。
  2. 建立壞掉的大頭菜,並且複製 10 次,檢查每次大頭菜是否都是壞掉的大頭菜,而且都賣不了錢。
/**
 * Class PrototypePatternTest.
 */
class PrototypePatternTest extends TestCase
{
    /**
     * 建立大頭菜,並且複製 10 次,
     * 檢查每次大頭菜是否都是大頭菜,而且價格是正確的。
     * 
     * @test
     */
    public function test_can_clone_turnips()
    {
        $turnips = new Turnips(100, 40);

        for ($i = 0; $i < 10; $i++) {
            $_turnips = clone $turnips;

            $this->assertInstanceOf(Turnips::class, $_turnips);
            $this->assertEquals(4000, $_turnips->calculatePrice());
        }
    }

    /**
     * 建立壞掉的大頭菜,並且複製 10 次,
     * 檢查每次大頭菜是否都是壞掉的大頭菜,而且都賣不了錢。
     * 
     * @test
     */
    public function test_can_clone_spoiled_turnips()
    {
        $turnips = new SpoiledTurnips(100, 40);

        for ($i = 0; $i < 10; $i++) {
            $_turnips = clone $turnips;
            $this->assertInstanceOf(SpoiledTurnips::class, $_turnips);
            $this->assertEquals(0, $_turnips->calculatePrice());
        }
    }
}

最後測試的執行結果會獲得如下:

PHPUnit Pretty Result Printer 0.28.0 by Codedungeon and contributors.
==> Configuration: ~/php-design-pattern/vendor/codedungeon/phpunit-result-printer/src/phpunit-printer.yml

PHPUnit 9.2.6 by Sebastian Bergmann and contributors.


 ==> AbstractFactoryTest        ✔  ✔  ✔  ✔  
 ==> BuilderPatternTest         ✔  ✔  
 ==> FactoryMethodTest          ✔  ✔  ✔  ✔  
 ==> PoolPatternTest            ✔  ✔  
 ==> PrototypePatternTest       ✔  ✔  
 ==> SimpleFactoryTest          ✔  ✔  ✔  ✔  
 ==> SingletonPatternTest       ✔  
 ==> StaticFactoryTest          ✔  ✔  ✔  ✔  ✔  

Time: 00:00.050, Memory: 6.00 MB

OK (24 tests, 68 assertions)

完整程式碼

設計模式不難,找回快樂而已,以大頭菜為例。

參考文獻


上一篇
【PHP 設計模式大頭菜】工廠方法 Factory Method
下一篇
【PHP 設計模式大頭菜】建造者模式 Builder Pattern
系列文
設計模式不難,找回快樂而已,以大頭菜為例。30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言